home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Games of Daze
/
Infomagic - Games of Daze (Summer 1995) (Disc 1 of 2).iso
/
gnuish
/
swalibas
/
swap.c
< prev
next >
Wrap
C/C++ Source or Header
|
1990-09-10
|
31KB
|
1,226 lines
/* swap.c - swap parent to disk, EMS, or XMS while executing child (MS-DOS)
Copyright (C) 1990 by Thorsten Ohl, td12@ddagsi3.bitnet
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 1, or (at your option)
any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
static char RCS_id[] =
"$Header: e:/gnu/swaplib/RCS/swap.c'v 0.9 90/09/09 21:43:50 tho Stable $";
/* Please note the following naming convention:
* functions and variables beginning with "_swap_kernel_" do *not*
refer to functions and variables in the text and data segments
which are swapped out. */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <errno.h>
#include <signal.h>
#include <sys/types.h>
#include "swaplib.h"
#include <dos.h>
/* Macros for dealing with Intel "huge" pointers. */
/* Add OFF to PTR, taking care of segment overflow */
#define FP_ADD(ptr,off) \
(FP_SEG (ptr) += (off) >> 4, FP_OFF (ptr) += (off) & 0xff, ptr)
/* Paragraph-align PTR. */
#define FP_PARA_ALIGN(ptr) \
{ \
FP_SEG (ptr) \
+= ((FP_OFF (ptr) + 15) >> 4) + 1; \
FP_OFF (ptr) = 0x0000; \
}
/* don't call MS' stack checker (it will fail with the local stack!) */
#pragma check_stack (off)
#define FILE_IO_BLKSIZE 0x8000
#define MAX_MSDOS_CMDLINE 126
#define MAX_MSDOS_PATH 144
#define MAX_MSDOS_MCBS 25
/* Atribute to force storage in the code segment! */
#define CODE _based (_segname ("_CODE"))
/* The size of the code and data swapped out in bytes. */
static off_t CODE _swap_kernel_swapped_bytes;
/* A XMS, EMS or disk handle for the swapped program. */
static unsigned int CODE _swap_kernel_handle;
/* A safe copy of _PSP. */
static unsigned int CODE _swap_kernel_psp;
/* The size of the swap kernel in paragraphs (16b). */
static unsigned int CODE _swap_kernel_resident_paras;
/* The size of the first block (which contains the kernel). */
static unsigned int CODE _swap_kernel_first_block_paras;
/* The environment actually passed to the child (starts right
after the kernel). */
static char _far * CODE _swap_kernel_environment;
/* The environment prepared for the child (will be copied
to _SWAP_KERNEL_ENVIRONMENT). */
static char _far * CODE _swap_kernel_environment_ptr;
#ifdef USE_EMS
/* Pointer to the EMS pageframe. */
static char _far * CODE _swap_kernel_ems_page_frame;
#endif
/* Parameters for DOS for creating a child process */
#pragma pack (1)
static struct
{
_segment environment_segment;
char _far *cmd_line_ptr;
char _far *fcb_ptr_1;
char _far *fcb_ptr_2;
} CODE _swap_kernel_parameter_block;
#pragma pack ()
static char CODE _swap_kernel_path[MAX_MSDOS_PATH];
static char CODE _swap_kernel_cmdline[MAX_MSDOS_CMDLINE + 2];
static char CODE _swap_kernel_fcb_1[16]; /* FCBs for DOS. */
static char CODE _swap_kernel_fcb_2[16];
static unsigned int CODE _swap_kernel_environ_seg;
static unsigned int CODE _swap_kernel_environment_size;
static int CODE _swap_kernel_return_code;
static struct
{
_segment loc;
unsigned int len;
} CODE _swap_kernel_orig_mcbs[MAX_MSDOS_MCBS];
static int (*CODE _swap_kernel_swap_in) (int handle, void _far * buffer,
long bytes);
static int (*CODE _swap_kernel_swap_out) (int handle, void _far * buffer,
long bytes);
#ifdef USE_XMS
static char _far * CODE _swap_kernel_xms_control;
#pragma pack (1)
static struct
{
long length;
int src_handle;
long src_offset;
int dest_handle;
long dest_offset;
} CODE _swap_kernel_xms_move_table;
#pragma pack ()
#endif /* USE_XMS */
/* The local stack */
#define STACK_SIZE 0x200
static char CODE _swap_kernel_local_stack[STACK_SIZE];
static unsigned int CODE _swap_kernel_stack_pointer;
static _segment CODE _swap_kernel_stack_segment;
static void _swap_kernel_end (void);
/* "Save" version of memcpy. */
static void _far * _swap_kernel_memcpy (void _far *destination,
const void _far *source, size_t bytes);
/* MS-DOS interface */
#pragma pack (1) /* won't fit, if we don't pack! */
struct mcb_info
{
char id_byte; /* 'M': not, 'Z': last MCB */
unsigned short owner; /* PSP of owner */
unsigned short length; /* length (in paragraphs = 16b) */
};
#pragma pack ()
static void _swap_kernel_fatal_error (char code, char CODE *msg, size_t len);
static int _swap_kernel_free_block (_segment block);
static int _swap_kernel_set_block (_segment block, unsigned int paras);
static unsigned int _swap_kernel_allocate_block (unsigned int paras);
static void _swap_kernel_free_upper_blocks (void);
static void _swap_kernel_reclaim_blocks (void);
static int _swap_kernel_exec (void);
/* Disk I/O */
static unsigned int _swap_kernel_read (int handle, void _far * buffer,
size_t bytes);
static unsigned int _swap_kernel_write (int handle, void _far * buffer,
size_t bytes);
static int _swap_kernel_rewind (int handle);
static int _swap_kernel_write_to_handle (int handle, void _far * buffer,
long size);
static int _swap_kernel_read_from_handle (int handle, void _far * buffer,
long size);
/* XMS */
#ifdef USE_XMS
static int _swap_kernel_xms_move_out (int handle, void _far * buffer,
long bytes);
static int _swap_kernel_xms_move_in (int handle, void _far * buffer,
long bytes);
static int _swap_kernel_xms_move (void);
static int xms_installed (void);
static void xms_get_control_function (void);
static unsigned int xms_allocate_memory (unsigned int kilobytes);
static unsigned int xms_free_memory (unsigned int handle);
#endif /* USE_XMS */
/* EMS */
#ifdef USE_EMS
static int _swap_kernel_ems_save_page_map (int handle);
static int _swap_kernel_ems_restore_page_map (int handle);
static int _swap_kernel_ems_map_logical_page (int handle, int logical_page);
static int _swap_kernel_ems_move_out (int handle, void _far *buffer,
long bytes);
static int _swap_kernel_ems_move_in (int handle, void _far *buffer,
long bytes);
static int ems_present (void);
static int ems_alloc_pages (int n);
static int ems_free_pages (unsigned int handle);
static void _far *ems_get_page_frame (void);
#endif /* USE_EMS */
/* Signal handling */
typedef void _interrupt _far signal_handler (void);
static signal_handler *_swap_kernel_caller_int23;
static void _far _swap_kernel_int23_handler (void);
static int CODE _swap_kernel_user_interrupt = 0; /* record interrupts */
static char CODE _swap_kernel_int23_message[] = "\r\n\a*** Interrupt.\r\n";
/* "Higher" level code. */
static void _swap_kernel_setup_environment (void);
static int _swap_kernel_spawn_child (void);
static void install_parameters (char *path, char *cmdline, char *env,
size_t size);
static struct mcb_info far *last_mcb (void);
static int alloc_swap_kernel_file (char *name, long size);
static unsigned int cleanup_swap_kernel_file (unsigned handle, char *name);
/* Very Fatal Error messages. */
static char CODE _swap_kernel_err_msg_head[] = \
"\r\nFatal error in memory management. Aborting.\r\nReason: ";
static char CODE _swap_kernel_err_msg_0[] = "Can't reallocate core.";
static char CODE _swap_kernel_err_msg_1[] = "Can't swap code back.";
static char CODE _swap_kernel_err_msg_2[] = "Can't release core.";
static char CODE _swap_kernel_err_msg_3[] = "Too many MCBs.";
#define SWAP_FATAL_ERROR(num) \
_swap_kernel_fatal_error (-1, _swap_kernel_err_msg_##num, \
sizeof (_swap_kernel_err_msg_##num))
void
_swap_kernel_setup_environment (void)
{
_swap_kernel_resident_paras
= FP_SEG (_swap_kernel_environment) - _swap_kernel_psp;
if (_swap_kernel_environment_size && *_swap_kernel_environment_ptr)
{
_swap_kernel_memcpy (_swap_kernel_environment,
_swap_kernel_environment_ptr,
_swap_kernel_environment_size);
_swap_kernel_resident_paras += (_swap_kernel_environment_size + 15) >> 4;
_swap_kernel_environ_seg = FP_SEG (_swap_kernel_environment);
}
else
_swap_kernel_environ_seg = 0; /* pass our own environment */
}
static void _far *
_swap_kernel_memcpy (void _far *destination, const void _far *source,
size_t bytes)
{
_asm
{
push ds
mov cx,bytes
lds si,source
les di,destination
shr cx,1 /* save odd byte in carry flag */
rep movsw /* move words (for speed) */
adc cx,cx
rep movsb /* move the odd byte, if any */
pop ds
}
return destination;
}
/* Memory management.
WARNING: this used undocumented MS-DOS features.
This features seem to be very stable anyway (Microsoft obviously uses
them in their own programs and since they won't want to break them,
these feaatures shouldn't go away. */
/* Does this MCB belong to us? */
#define OUR_MCB(mcb) ((mcb)->owner == _swap_kernel_psp)
/* Return a pointer to OUR first MCB */
#define FIRST_MCB(mcb) \
(FP_SEG (mcb) = _swap_kernel_psp - 1, FP_OFF (mcb) = 0, mcb)
/* Return a pointer to the next MCB */
#define NEXT_MCB(mcb) \
(FP_SEG (mcb) = FP_SEG (mcb) + mcb->length + 1, mcb)
int
_swap_kernel_free_block (_segment block)
{
_asm
{
mov ax, block;
mov es, ax;
mov ah, 0x49 /* MS-DOS Free Allocated Memory */
int 0x21
jc failed
xor ax, ax; /* success */
failed:
}
}
int
_swap_kernel_set_block (_segment block, unsigned int paras)
{
_asm
{
mov ax, block
mov es, ax
mov bx, paras
mov ah, 4ah /* MS-DOS Set Block */
int 0x21
jc failed
xor ax, ax /* success */
failed:
}
}
static unsigned int
_swap_kernel_allocate_block (unsigned int paras)
{
_asm
{
mov bx, paras
mov ah, 0x48 /* MS-DOS Allocate Memory */
int 0x21
jnc done
mov ax, 0x0000 /* failed */
done:
}
}
/* Free, one by one, the memoy blocks owned by us. This excluded the
first block, which will be shrunk later. _swap_kernel_orig_mcbs will be
zero-terminated. */
void
_swap_kernel_free_upper_blocks (void)
{
int i = 0;
struct mcb_info far *mcb;
FIRST_MCB (mcb);
while (mcb->id_byte == 'M')
{
NEXT_MCB (mcb); /* leave the first block intact (for the moment) */
if (OUR_MCB (mcb))
{
if (i >= MAX_MSDOS_MCBS)
SWAP_FATAL_ERROR (3);
if (_swap_kernel_free_block (FP_SEG (mcb) + 1))
SWAP_FATAL_ERROR (2);
_swap_kernel_orig_mcbs[i].loc = FP_SEG (mcb) + 1;
_swap_kernel_orig_mcbs[i].len = mcb->length;
i++;
}
}
_swap_kernel_orig_mcbs[i].loc = 0x000;
}
/* Reclaim, one by one, the original memory blocks, as stored in
_swap_kernel_orig_mcbs. From the MS-DOS point of view, this should be not
necessary, since MS-DOS keeps (to my knowledge) no internal record of
the memory allocation and the original MCBs are restored together with
the image. But in this way we can catch the fatal condition when the
child has (illegally) left a resident grandchild. Also we will be
warned if our methos fails with future MS-DOS versions. */
void
_swap_kernel_reclaim_blocks (void)
{
int i = 0;
while (_swap_kernel_orig_mcbs[i].loc != 0x000)
if (_swap_kernel_allocate_block (_swap_kernel_orig_mcbs[i].len)
!= _swap_kernel_orig_mcbs[i].loc)
SWAP_FATAL_ERROR (0);
else
i++;
}
int
_swap_kernel_exec (void)
{
_swap_kernel_parameter_block.environment_segment
= _swap_kernel_environ_seg;
_swap_kernel_parameter_block.cmd_line_ptr
= (char _far *) &_swap_kernel_cmdline;
_swap_kernel_parameter_block.fcb_ptr_1
= (char _far *) &_swap_kernel_fcb_1;
_swap_kernel_parameter_block.fcb_ptr_2
= (char _far *) &_swap_kernel_fcb_2;
/* The compiler saves si and di by himself. */
_asm
{
push ds /* save ds */
mov ax, cs /* let es and ds point into code segment */
mov es, ax
mov ds, ax
mov si, offset _swap_kernel_cmdline /* parse commandline */
mov di, offset _swap_kernel_fcb_1 /* create first FCB */
mov ax, 0x2901 /* MS-DOS Parse File Name */
int 0x21
mov di, offset _swap_kernel_fcb_2 /* create second FCB */
mov ax, 0x2901 /* MS-DOS Parse File Name */
int 0x21
mov bx, offset _swap_kernel_parameter_block /* es:bx */
mov dx, offset _swap_kernel_path /* ds:dx */
mov ax, 0x4b00 /* MS-DOS Load and Execute Program */
int 21h
mov ax, 0ffffh /* assume failure */
jc failed
mov ah, 0x4d /* MS-DOS Get Return Code of Child */
int 21h
mov _swap_kernel_return_code, ax /* store return code */
failed:
pop ds /* restore ds */
}
}
int
_swap_kernel_spawn_child (void) /* CAN'T TAKE PARAMETERS! */
{
/* void */ /* CAN'T HAVE LOCAL VARIABLES! */
/* FROM HERE ON: DON'T REFER TO THE GLOBAL STACK! */
_asm
{
/* save stack position */
mov cs:_swap_kernel_stack_pointer, sp
mov cs:_swap_kernel_stack_segment, ss
/* Interrupts off */
cli
/* Change stack */
mov ax, seg _swap_kernel_local_stack
mov ss, ax
mov sp, offset _swap_kernel_local_stack + STACK_SIZE
/* Interrupts on */
sti
}
if ((*_swap_kernel_swap_out) (_swap_kernel_handle, _swap_kernel_environment,
_swap_kernel_swapped_bytes))
return -1;
_swap_kernel_setup_environment ();
_swap_kernel_free_upper_blocks ();
_swap_kernel_set_block (_swap_kernel_psp, _swap_kernel_resident_paras);
_swap_kernel_exec (); /* !!! BIG DEAL !!! */
if (_swap_kernel_set_block (_swap_kernel_psp,
_swap_kernel_first_block_paras))
SWAP_FATAL_ERROR (0);
_swap_kernel_reclaim_blocks ();
if ((*_swap_kernel_swap_in) (_swap_kernel_handle, _swap_kernel_environment,
_swap_kernel_swapped_bytes))
SWAP_FATAL_ERROR (1);
_asm
{
/* get saved stack position */
mov ax, cs:_swap_kernel_stack_pointer
mov bx, cs:_swap_kernel_stack_segment
/* Interrupts off */
cli
/* Change stack */
mov ss, bx
mov sp, ax
/* Interrupts on */
sti
}
/* THE GLOBAL STACK IS SAVE AGAIN! */
return _swap_kernel_return_code;
}
/* Display LEN bytes from string MSG and *immediately* return to DOS,
with CODE as return code. This is a panic exit, only to be used
as a last resort. ~~~~~~~~~~ */
void
_swap_kernel_fatal_error (char code, char CODE *msg, size_t len)
{
_asm
{
mov ax, cs /* ds = cs */
mov ds, ax
mov bx, 0x02 /* /dev/stderr */
mov dx, offset _swap_kernel_err_msg_head
mov cx, length _swap_kernel_err_msg_head
mov ah, 0x40 /* MS-DOS Write Handle */
int 0x21
mov dx, msg /* message */
mov cx, len /* length */
mov ah, 0x40
int 0x21
mov al, code /* bail out */
mov ah, 0x4c /* MS-DOS End Process */
int 0x21
}
}
/* Lowest level disk I/0: */
/* Write SIZE bytes from BUFFER to HANDLE. Returns 0 on success, -1 on
failure. */
int
_swap_kernel_write_to_handle (int handle, void _far *buffer, off_t size)
{
while (size > 0L)
{
size_t bytes = (size_t) min (size, FILE_IO_BLKSIZE);
size_t bytes_written = _swap_kernel_write (handle, buffer, bytes);
if (bytes_written != bytes)
return -1;
FP_ADD (buffer, bytes);
size -= bytes;
}
return 0;
}
size_t
_swap_kernel_write (int handle, void _far *buffer, size_t bytes)
{
_asm
{
push ds
mov dx, word ptr buffer /* offset */
mov ax, word ptr buffer + 2 /* segment */
mov ds, ax
mov bx, handle
mov cx, bytes
mov ah, 0x40 /* MS-DOS Write Handle */
int 0x21
jnc done
mov ax, 0xffff
done:
pop ds
}
}
/* Read SIZE bytes from HANDLE to BUFFER. Returns 0 on success, -1 on
failure. */
int
_swap_kernel_read_from_handle (int handle, void _far *buffer, off_t size)
{
_swap_kernel_rewind (handle);
while (size > 0L)
{
size_t bytes = (size_t) min (size, FILE_IO_BLKSIZE);
size_t bytes_read = _swap_kernel_read (handle, buffer, bytes);
if (bytes_read != bytes)
return -1;
FP_ADD (buffer, bytes);
size -= bytes;
}
return 0;
}
size_t
_swap_kernel_read (int handle, void _far *buffer, size_t bytes)
{
_asm
{
push ds
mov dx, word ptr buffer /* offset */
mov ax, word ptr buffer + 2 /* segment */
mov ds, ax
mov bx, handle
mov cx, bytes
mov ah, 0x3f /* MS-DOS Read Handle */
int 0x21
jnc done
mov ax, 0xffff
done:
pop ds
}
}
/* Rewind the file pointer for HANDLE to the beginning of the file. */
int
_swap_kernel_rewind (int handle)
{
_asm
{
mov bx, handle
mov cx, 0x0000 /* offset = 0 */
mov dx, 0x0000
mov ax, 0x4200 /* MS-DOS Move File Pointer, (beginning) */
int 0x21
jc failed
mov ax, 0x0000
failed:
}
}
#ifdef USE_XMS
/* XMS interface */
int
_swap_kernel_xms_move_out (int handle, void _far *buffer, long bytes)
{
_swap_kernel_xms_move_table.length = bytes;
_swap_kernel_xms_move_table.src_handle = 0x0000;
_swap_kernel_xms_move_table.src_offset = (long) buffer;
_swap_kernel_xms_move_table.dest_handle = handle;
_swap_kernel_xms_move_table.dest_offset = 0L;
_swap_kernel_xms_move ();
}
int
_swap_kernel_xms_move_in (int handle, void _far *buffer, long bytes)
{
_swap_kernel_xms_move_table.length = bytes;
_swap_kernel_xms_move_table.dest_handle = 0x0000;
_swap_kernel_xms_move_table.dest_offset = (long) buffer;
_swap_kernel_xms_move_table.src_handle = handle;
_swap_kernel_xms_move_table.src_offset = 0L;
_swap_kernel_xms_move ();
}
int
_swap_kernel_xms_move (void)
{
_asm
{
push ds
mov si, offset _swap_kernel_xms_move_table
mov ax, seg _swap_kernel_xms_move_table
mov ds, ax
mov ah, 0x0b
call far ptr cs:[_swap_kernel_xms_control]
cmp ax, 0x0001
jne failed
mov ax, 0x0000
jmp done
failed:
mov ax, 0xffff
done:
pop ds
}
}
#endif /* USE_XMS */
#ifdef USE_EMS
/* EMS interface */
#define PHYSICAL_PAGE 0x00
int
_swap_kernel_ems_move_out (int handle, void _far *buffer, long bytes)
{
void *ptr = buffer;
int logical_page = 0;
_swap_kernel_ems_save_page_map (handle);
while (bytes > 0L)
{
size_t n = (size_t) min (bytes, 0x4000L);
if (_swap_kernel_ems_map_logical_page (handle, logical_page++))
return -1;
_swap_kernel_memcpy (_swap_kernel_ems_page_frame, ptr, n);
bytes -= (long) n;
FP_ADD (ptr, n);
}
_swap_kernel_ems_restore_page_map (handle);
return 0;
}
int
_swap_kernel_ems_move_in (int handle, void _far *buffer, long bytes)
{
void *ptr = buffer;
int logical_page = 0;
_swap_kernel_ems_save_page_map (handle);
while (bytes > 0L)
{
size_t n = (size_t) min (bytes, 0x4000L);
if (_swap_kernel_ems_map_logical_page (handle, logical_page++))
return -1;
_swap_kernel_memcpy (ptr, _swap_kernel_ems_page_frame, n);
bytes -= (long) n;
FP_ADD (ptr, n);
}
_swap_kernel_ems_restore_page_map (handle);
return 0;
}
int
_swap_kernel_ems_map_logical_page (int handle, int logical_page)
{
_asm
{
mov dx, handle
mov bx, logical_page
mov ax, 0x4400 + PHYSICAL_PAGE /* EMS Map Page */
int 0x67
mov cl, 8 /* "mov ax, ah" */
shr ax, cl
}
}
int
_swap_kernel_ems_save_page_map (int handle)
{
_asm
{
mov dx, handle
mov ah, 0x47 /* EMS Save Page Map */
int 0x67
mov cl, 8 /* "mov ax, ah" */
shr ax, cl
}
}
int
_swap_kernel_ems_restore_page_map (int handle)
{
_asm
{
mov dx, handle
mov ah, 0x48 /* EMS Restore Page Map */
int 0x67
mov cl, 8 /* "mov ax, ah" */
shr ax, cl
}
}
#endif /* USE_EMS */
/* Signal handling */
/* Simple ^C "handler" that displays a short message and instructs
MS-DOS to abort the child. We also set a flag _swap_kernel_USER_INTERRUPT
which will be used to determine whether such an event occured.
(This is *very* useful if the child doesn't give a reasonable
exit code.)
Note: resetting the C library signals is NOT enough, since even
the default handlers (once a non trivial handler has been installed)
go through the library code.
Note: make sure that your compiler generates exactly the stackframe we
undo before the retf. */
void _far
_swap_kernel_int23_handler (void)
{
/* Record that we've gone through this. */
_swap_kernel_user_interrupt = 1;
_asm
{
push ax /* Carefully save all registers. */
push bx
push cx
push dx
push ds
pushf
sti /* want to access DOS */
push cs /* ds = cs */
pop ds
mov bx, 0x02 /* /dev/stderr */
mov dx, offset _swap_kernel_int23_message
mov cx, length _swap_kernel_int23_message
mov ah, 0x40 /* MS-DOS Write Handle */
int 0x21
popf /* Carefully restore all registers. */
pop ds
pop dx
pop cx
pop bx
pop ax
pop si /* Undo the C stackframe. */
pop di
pop bp
stc /* The `Carry Flag' tells DOS to kill the */
retf /* child (that's why we don't do an `iret'). */
}
}
/* The resident part ends here. */
void
_swap_kernel_end (void)
{
/* NOP */
}
/* The transient part starts here. */
#pragma check_stack ()
/* Install the global parameters. Execute this as the first function, since
some macros need _swap_kernel_psp with the correct value. */
void
install_parameters (char *path, char *cmdline, char *env, size_t size)
{
size_t len = strlen (cmdline);
struct mcb_info far *mcb;
_fstrcpy ((char _far *) _swap_kernel_path, (char _far *) path);
*_swap_kernel_cmdline = (char) len;
_fstrcpy ((char _far *) _swap_kernel_cmdline + 1, (char _far *) cmdline);
_swap_kernel_cmdline[len+1] = '\r';
_swap_kernel_environment_ptr = env; /* this will be copied later */
_swap_kernel_environment_size = size;
_swap_kernel_psp = _psp; /* put them into a save place. */
_swap_kernel_first_block_paras = FIRST_MCB (mcb)->length;
}
/* Allocate a swap file named NAME, making sure that at least SIZE bytes
are available on the disk. Returns a MS-DOS handle (not to be
confused with a C file-descriptor!). */
int
alloc_swap_kernel_file (char *name, off_t size)
{
struct diskfree_t disk_free;
unsigned drive;
off_t free;
int handle;
if (name == NULL || *name == '\0') /* could create filename ourselves. */
return -1;
if (name[1] == ':')
drive = tolower (*name) - 'a' + 1;
else
/* Get current drive. */
_dos_getdrive (&drive);
_dos_getdiskfree (drive, &disk_free);
free = (off_t) disk_free.avail_clusters *
(off_t) disk_free.sectors_per_cluster * (off_t) disk_free.bytes_per_sector;
if (free < size)
return (-1);
if (_dos_creat (name, _A_NORMAL, &handle))
return (-1);
else
return handle;
}
/* Close and delete the temporary file. */
unsigned int
cleanup_swap_kernel_file (unsigned int handle, char *name)
{
return !_dos_close (handle) && !unlink (name);
}
#ifdef USE_XMS
/* More XMS */
/* Microsoft's recommendation: */
int
xms_installed (void)
{
_asm
{
mov ax, 0x4300
int 0x2f
cmp al, 0x80
jne failed
mov ax, 0x0001
jmp done
failed:
mov ax, 0x0000
done:
}
}
void
xms_get_control_function (void)
{
_asm
{
mov ax, 0x4310
int 0x2f
mov word ptr cs:_swap_kernel_xms_control, bx
mov bx, es
mov word ptr cs:_swap_kernel_xms_control + 2, bx
}
}
unsigned int
xms_allocate_memory (unsigned int kilobytes)
{
_asm
{
mov dx, kilobytes
mov ah, 0x09
call far ptr cs:[_swap_kernel_xms_control]
cmp ax, 0x0001
jne failed
mov ax, dx
jmp done
failed:
mov ax, 0xffff
done:
}
}
unsigned int
xms_free_memory (unsigned int handle)
{
_asm
{
mov dx, handle
mov ah, 0x0a
call far ptr cs:[_swap_kernel_xms_control]
cmp ax, 0x0001
je done
mov ax, 0x0000
done:
}
}
#endif /* USE_XMS */
#ifdef USE_EMS
/* More EMS */
/* Test for presence of LIM EMS 4.0.
(this procedure is taken from the LIM specification). */
int
ems_present (void)
{
#ifndef BROKEN_EMM
static char _far ems_id[] = "EMMXXXX0"; /* LIM EMS 4.0 identification. */
#else
/* This is for debugging only, don't worry about it ... */
static char _far ems_id[] = "Junk-EMM";
#endif
char _far *ems_device = (char _far *) _dos_getvect (0x67);
FP_OFF (ems_device) = 0x000a;
return !_fstrncmp (ems_id, ems_device, sizeof (ems_id) - 1);
}
/* Allocate pages from the EMS Manager. Returns handle or -1 no error. */
int
ems_alloc_pages (int n)
{
_asm
{
mov bx, n
mov ah, 0x43 /* EMS Allocate Pages */
int 0x67
cmp ah, 0x00
jz success
mov ax, 0xffff /* failure */
ret
success:
mov ax, dx /* return handle */
}
}
/* Free pages allocated for HANDLE. Returns 0 if successful. */
int
ems_free_pages (unsigned int handle)
{
_asm
{
mov dx, handle
mov ah, 0x45 /* EMS Free Pages */
int 0x67
mov cl, 8 /* "mov ax, ah" */
shr ax, cl
}
}
/* Return far pointer to EMS page frame. */
void _far *
ems_get_page_frame (void)
{
void _far *frame = (void _far *) 0;
_asm
{
mov ah, 0x41 /* EMS Page Frame */
int 0x67
cmp ah, 0x00
jz success
ret /* failure */
success:
mov word ptr frame + 2, bx /* segment of page frame */
}
return frame;
}
#endif /* USE_EMS */
/* Return the last MCB owned by us.
WARNING: This assumes that _swap_kernel_psp has already been set to _PSP
(e.g. by install_parameters()) */
struct mcb_info far *
last_mcb (void)
{
struct mcb_info far *mcb;
struct mcb_info far *ret;
FIRST_MCB (mcb);
while (mcb->id_byte == 'M')
{
if (OUR_MCB (mcb))
ret = NEXT_MCB (mcb);
else
NEXT_MCB (mcb);
}
if (mcb->id_byte == 'Z') /* found the end */
return ret;
else /* error */
return NULL;
}
/* MODE is the preferred swapping mode, if XMS or EMS are requested but not
available, it is mapped to DISK. PATH is the complete path of the program
to be executed, it must not be longer than MAX_MSDOS_PATH (=144). CMDLINE
is the commandline to be passed to the program, it must not be longer than
MAX_MSDOS_CMDLINE (=126). ENV is a well formed MS-DOS environment of
length LEN, including the terminating '\0's. FILE is a valid filename,
which will be used for a possible disk swap file. */
int
_swap_spawn_child (enum swapping_mode mode, char *path, char *cmdline,
char *env, int len, char *file)
{
int rc;
unsigned int (*cleanup_function) (unsigned int handle,...);
install_parameters (path, cmdline, env, len);
_swap_kernel_environment = (char _far *) _swap_kernel_end;
FP_PARA_ALIGN (_swap_kernel_environment);
_swap_kernel_swapped_bytes = (long) ((char _huge *) last_mcb ()
- (char _huge *) _swap_kernel_environment);
switch (mode)
{
case xms:
#ifdef USE_XMS
if (xms_installed ())
{
xms_get_control_function ();
_swap_kernel_swap_out = _swap_kernel_xms_move_out;
_swap_kernel_swap_in = _swap_kernel_xms_move_in;
cleanup_function = xms_free_memory;
_swap_kernel_handle
= xms_allocate_memory ((unsigned int)
((_swap_kernel_swapped_bytes - 1) >> 10)
+ 1);
if (_swap_kernel_handle != -1)
break;
}
#endif /* USE_XMS */
/* fall through */
case ems:
#ifdef USE_EMS
if (ems_present ())
{
_swap_kernel_swap_out = _swap_kernel_ems_move_out;
_swap_kernel_swap_in = _swap_kernel_ems_move_in;
cleanup_function = ems_free_pages;
_swap_kernel_ems_page_frame = ems_get_page_frame ();
_swap_kernel_handle
= ems_alloc_pages ((unsigned int)
((_swap_kernel_swapped_bytes - 1) >> 14) + 1);
if (_swap_kernel_ems_page_frame && _swap_kernel_handle != -1)
break;
}
#endif /* USE_EMS */
/* fall through */
case disk:
_swap_kernel_swap_out = _swap_kernel_write_to_handle;
_swap_kernel_swap_in = _swap_kernel_read_from_handle;
cleanup_function = cleanup_swap_kernel_file;
_swap_kernel_handle
= alloc_swap_kernel_file (file, _swap_kernel_swapped_bytes);
if (_swap_kernel_handle != -1)
break;
errno = ENOSPC;
return -1;
case none:
/* FIX ME (i.e. code me) */
errno = EINVAL;
return -1;
}
_swap_kernel_user_interrupt = 0;
/* temporarily disable ^C */
_swap_kernel_caller_int23 = _dos_getvect (0x23);
_dos_setvect (0x23, (signal_handler *) _swap_kernel_int23_handler);
rc = _swap_kernel_spawn_child ();
/* did the user hit ^C ? */
if (_swap_kernel_user_interrupt)
rc |= (SIGINT << 8);
_dos_setvect (0x23, _swap_kernel_caller_int23);
cleanup_function (_swap_kernel_handle, file);
return rc;
}
/*
* Local Variables:
* mode:C
* minor-mode:auto-fill
* ChangeLog:ChangeLog
* compile-command:make
* End:
*/